package runner

import (
	"concurrent_runner/pkg"
	"context"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
)

var AppPath string
var LogPath string

const OsPathSeparator = string(os.PathSeparator)

func init() {
	file, _ := exec.LookPath(os.Args[0])
	path, _ := filepath.Abs(file)
	index := strings.LastIndex(path, OsPathSeparator)
	AppPath = path[:index]
	LogPath = fmt.Sprintf("%s%slogs", AppPath, OsPathSeparator)
	pkg.CreatePath(LogPath)
}

// ProcessData 执行命令数据
type ProcessData struct {
	Data    string
	Number  int
	Command string
	Params  []string
	ExecDir string
	Status  int
}

func GetProcessData(data string) (*ProcessData, error) {
	p := &ProcessData{
		Data: data,
	}
	err := p.AnalysisCommand()
	if err != nil {
		return nil, err
	}
	return p, nil
}

func (p *ProcessData) AnalysisCommand() error {
	arr := strings.Split(p.Data, " ")
	if len(arr) < 2 {
		return errors.New(fmt.Sprintf("解析命令失败:%s", p.Data))
	}
	n, err := strconv.Atoi(arr[0])
	if err != nil {
		return err
	}
	p.Number = n
	p.Command = arr[1]
	if len(arr) > 2 {
		p.Params = arr[2:]
	}
	return nil
}

func (p *ProcessData) Run(idx int, group *sync.WaitGroup) error {
	defer group.Done()
	group.Add(1)
	s := strings.ReplaceAll(p.Command, OsPathSeparator, "_")
	logFile := fmt.Sprintf("%s/%s.%d.log", LogPath, s, idx)
	stdout, err := os.OpenFile(logFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, os.ModeAppend|os.ModePerm)
	if err != nil {
		return errors.New("无法创建输出文件")
	}
	fmt.Println(fmt.Sprintf("执行[%d]:%s %+v", idx, p.Command, p.Params))
	pp, err := pkg.ForkChild(stdout, stdout, p.Command, p.Params, p.ExecDir)
	if err != nil {
		return err
	}
	pp.Wait()
	return nil
}

//ProcessPool 池子
type ProcessPool struct {
	ProcessDataEntity chan *ProcessData
	PoolSize          int
	Ctx               context.Context
}

func GetProcessPool(size int, ctx context.Context) *ProcessPool {
	return &ProcessPool{
		ProcessDataEntity: make(chan *ProcessData, size),
		PoolSize:          size,
		Ctx:               ctx,
	}
}

func (p *ProcessPool) Do(pd *ProcessData, group *sync.WaitGroup) {
	defer group.Done()
	group.Add(1)
	for i := 0; i < pd.Number; i++ {
		go pd.Run(i, group)
	}
}
